<?php

namespace common\service;

use common\exception\LogicException;
use common\model\DelayOrderModel;
use common\model\DelayOrderTaskModel;
use common\model\EntityModel;
use common\model\PlanModel;
use common\model\ProjectModel;
use common\model\ReviewRequestModel;
use common\model\StepCategoryModel;
use common\model\TaskMemberModel;
use common\model\TaskModel;
use common\model\TaskRelationTagModel;
use common\model\UserModel;
use support\ErrorCode;
use support\SingletonTrait;
use think\Exception;
use think\Hook;
use Throwable;
use Webman\Stomp\Client;

class DelayOrderService
{
    use SingletonTrait;

    /**
     * 待延期申请任务分组列表
     * @param $taskId
     * @return array
     */
    public function pendingDelayTaskGroupList($taskId)
    {
        $fields = [
            "task.id", "task.name as task_name", "task.project_id", "task.step_type", "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_delay_sum')) as task_delay_sum", 'task.episode_id', 'task.step_category_id',
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as status", "task.plan_end_time", 'task.assign_status',
            "entity.initial_episode_code", "entity.showings_number", "entity.name", "task.plan_delivery_time", "entity.id AS entity_id"
        ];

        $todType = TaskModel::STEP_TYPE_TODO_GROUP;
        $todType[] = TaskModel::STEP_TYPE_STEP;

        $taskList = model(TaskModel::class)
            ->join("entity on entity.id = task.entity_id", "LEFT")
            ->where(['task.id' => ["IN", $taskId], 'task.step_type' => ["IN", $todType]])
            ->field($fields)
            ->select();
        if (empty($taskList)) {
            return [];
        }

        ProducerManageService::getInstance()->getTaskAbnormalDetail($taskList);

        $taskGroup = [];
        $projectIds = [];
        $episodeStepCategoryFilter = [];
        $episodeIds = [];
        $stepCategoryIds = [];
        $todoTaskIds = [];
        $todoTaskMap = [];
        foreach ($taskList as $taskItem) {
            $taskItem['task_delay_sum'] = $taskItem['task_delay_sum'] ?? 0;
            if (!empty($taskItem['project_id'])) {
                $projectIds[$taskItem['project_id']] = $taskItem['project_id'];
            }

            if (!empty($taskItem['episode_id']) && !empty($taskItem['step_category_id'])) {
                $episodeStepCategoryFilter[$taskItem['episode_id'] . "_" . $taskItem['step_category_id']] = [
                    'plan.entity_id' => $taskItem['episode_id'],
                    's_tag.link_id' => $taskItem['step_category_id'],
                ];
            }

            if ($taskItem['step_type'] == TaskModel::STEP_TYPE_STEP) {
                $taskGroup[$taskItem['episode_id']][$taskItem["step_category_id"]][] = $taskItem;
            } elseif (in_array($taskItem['step_type'], TaskModel::STEP_TYPE_TODO_GROUP)) { //待办的 需要额外查询,环节与集id不在task表上
                $todoTaskIds[] = $taskItem['id'];
                $todoTaskMap[$taskItem['id']] = $taskItem;
            }

            if (!empty($taskItem['episode_id'])) {
                $episodeIds[$taskItem['episode_id']] = $taskItem['episode_id'];
            }

            if (!empty($taskItem['step_category_id'])) {
                $stepCategoryIds[$taskItem['step_category_id']] = $taskItem['step_category_id'];
            }
        }

        if (!empty($todoTaskIds)) {
            $taskRelationTagList = model(TaskRelationTagModel::class)
                ->where(['task_id' => ["IN", $todoTaskIds], 'link_module_code' => ["IN", ['show_step_category', 'step_category', 'episode']]])
                ->field("task_id,link_id,link_module_code")
                ->select();
            $taskRelationTagGroup = array_group_by($taskRelationTagList, 'task_id');
            foreach ($todoTaskIds as $taskId) {
                $currentStepCategoryId = 0;
                $currentEpisodeId = 0;
                foreach ($taskRelationTagGroup[$taskId] ?? [] as $taskRelationTagGroupItem) {
                    if (in_array($taskRelationTagGroupItem['link_module_code'], ['show_step_category', 'step_category'])) {
                        $currentStepCategoryId = $taskRelationTagGroupItem['link_id'];
                        $stepCategoryIds[$currentStepCategoryId] = $currentStepCategoryId;
                    } elseif ($taskRelationTagGroupItem['link_module_code'] == 'episode') {
                        $currentEpisodeId = $taskRelationTagGroupItem['link_id'];
                        $episodeIds[$currentEpisodeId] = $currentEpisodeId;
                    }
                }
                if (empty($currentEpisodeId) || empty($currentStepCategoryId)) {
                    continue;
                }
                $episodeStepCategoryFilter[$currentEpisodeId . "_" . $currentStepCategoryId] = [
                    'plan.entity_id' => $currentEpisodeId,
                    's_tag.link_id' => $currentStepCategoryId,
                ];
                $taskGroup[$currentEpisodeId][$currentStepCategoryId][] = $todoTaskMap[$taskId];
            }
        }

        $projectIds = array_values($projectIds);

        $episodeMap = [];
        if (!empty($episodeIds)) {
            $episodeIds = array_values($episodeIds);
            $episodeList = model(EntityModel::class)
                ->where(["id" => ["IN", $episodeIds]])
                ->field("id,name,project_id")
                ->select();
            $episodeMap = array_column($episodeList, null, 'id');
        }

        $stepCategoryMap = [];
        if (!empty($stepCategoryIds)) {
            $stepCategoryList = model(StepCategoryModel::class)
                ->where(['id' => ["IN", $stepCategoryIds]])
                ->field("id,name,code")
                ->select();
            $stepCategoryMap = array_column($stepCategoryList, null, 'id');
        }

        $planMap = [];
        if (!empty($episodeStepCategoryFilter)) {
            $episodeStepCategoryFilter = array_values($episodeStepCategoryFilter);
            $episodeStepCategoryFilter["_logic"] = "OR";
            $episodeStepCategoryFilter = [
                $episodeStepCategoryFilter,
            ];
            $planList = model(PlanModel::class)
                ->join("task_relation_tag s_tag on s_tag.task_id = plan.task_id and s_tag.link_module_code in ('step_category','show_step_category')")
                ->where([
                    'plan.project_id' => ["IN", $projectIds],
                    'plan.type' => PlanModel::TYPE_STEP_CATEGORY,
                    'plan.scope' => PlanModel::SCOPE_SINGLE,
                    $episodeStepCategoryFilter,
                ])
                ->field("s_tag.link_id as step_category_id,plan.id,plan.entity_id,plan.end_time")
                ->select();

            foreach ($planList as $planItem) {
                $planMap[$planItem['entity_id']][$planItem['step_category_id']] = $planItem;
            }
        }
        $result = [];
        foreach ($taskGroup as $episodeId => $taskGroupArray) {
            foreach ($taskGroupArray as $stepCategoryId => $taskGroupItem) {
                $taskListTmp = [
                    'id' => $stepCategoryMap[$stepCategoryId]['id'] ?? 0,
                    'name' => $stepCategoryMap[$stepCategoryId]['name'] ?? '其他',
                    'code' => $stepCategoryMap[$stepCategoryId]['code'] ?? '其他',
                    'children' => $taskGroupItem,
                ];
                $tmp = [
                    'episode' => $episodeMap[$episodeId] ?? ['id' => 0, 'name' => '其他', 'project_id' => 0],
                    'project_id' => 0,
                    'step_category' => $stepCategoryMap[$stepCategoryId] ?? ['id' => 0, 'name' => '', 'code' => ''],
                    'plan' => $planMap[$episodeId][$stepCategoryId] ?? ['id' => 0, 'step_category_id' => 0, 'entity_id' => 0, 'end_time' => 0],
                    'task_list' => [$taskListTmp],
                ];
                if (!empty($tmp['episode'])) {
                    $tmp['project_id'] = $tmp['episode']['project_id'];
                }
                $result[] = $tmp;
            }
        }
        return $result;
    }

    /**
     * 获取单号 (年月日时分+三位随机码)
     * @param $model
     * @return string
     * @throws \Exception
     */
    public function getWorkNumber($model)
    {
        $oldNumber = [];
        while (true) {
            $number = date("YmdHi") . random_int(100, 999);
            if (isset($oldNumber[$number])) {
                continue;
            }
            $isExist = $model->where(['number' => $number])->find();
            if (empty($isExist)) {
                return $number;
            } else {
                $oldNumber[$number] = $number;
            }
        }
    }

    /**
     * 创建延期单
     * @param $data
     * @param $createdBy
     * @return array|bool
     * @throws Throwable
     */
    public function createDelayOrder($data, $createdBy)
    {
        //先创建延期单,再创建审核
        $delayOrderModel = model(DelayOrderModel::class);
        //单号
        $number = $this->getWorkNumber($delayOrderModel);
        $episodeId = $data['episode_id'] ?? 0;
        $stepCategoryId = $data['step_category_id'] ?? 0;
        $delayTaskList = $data['delay_task_list'];
        $reviewFeedbackId = $data['review_feedback_id'] ?? 0;

        $episode = [];
        $project = [];
        if (!empty($episodeId)) {
            $episode = model(EntityModel::class)->field("id,project_id,name")->find($episodeId);
            if (empty($episode)) {
                throw new LogicException('episode not found', ErrorCode::ENTITY_NOT_FOUND);
            }
            $project = model(ProjectModel::class)->find($episode['project_id']);
            if (empty($project)) {
                throw new LogicException('project not found', ErrorCode::PROJECT_NOT_FOUND);
            }
        }
        $assetOrShot = 'default';
        if (!empty($stepCategoryId)) {
            $stepCategory = StepCategoryService::getInstance()->getOne(['id' => $stepCategoryId], '*');
            $topStepCategory = StepCategoryService::getInstance()->getOne(['id' => $stepCategory['parent_id']], '*');
            $stepCategoryModule = module_by_id($topStepCategory['entity_id']);
            $assetOrShot = in_array($stepCategoryModule['code'], ['storyboard', 'shot']) ? 'shot' : 'asset';
        }
        $plan = [];
        $downstreamStepCategoryIdList = [];
        if (!empty($episodeId) && !empty($stepCategoryId)) {
            // 关联剧集、环节的，获取排期
            $plan = model(PlanModel::class)
                ->join("task_relation_tag s_tag on s_tag.task_id = plan.task_id and s_tag.link_module_code in ('step_category','show_step_category')")
                ->where([
                    'plan.project_id' => $episode['project_id'],
                    'plan.type' => PlanModel::TYPE_STEP_CATEGORY,
                    'plan.scope' => PlanModel::SCOPE_SINGLE,
                    'plan.entity_id' => $episode['id'],
                    's_tag.link_id' => $stepCategoryId,
                ])
                ->field("plan.id,plan.end_time")
                ->find();
            // 查询当前环节对应的下游
            $downstreamStepCategoryIdList = StepCategoryDownStreamService::getInstance()->get($project['id'], $stepCategoryId, []);
            array_unshift($downstreamStepCategoryIdList, $stepCategoryId);
            $downstreamStepCategoryIdList = array_values(array_unique($downstreamStepCategoryIdList));
        }

        $stepCategoryName = $stepCategory["name"] ?? '';
        $userName = model(UserModel::class)->where(["id" => $createdBy])->value("name");
        //名称 (任务所在)
        $name = join("/", array_filter([$episode["name"] ?? '', $stepCategoryName, $userName . $number], fn($item) => !empty($item)));
        $delayTaskData = [];
        $delayTaskIds = [];
        $todayDoneTaskIds = [];
        $requestDelayTaskIds = [];
        foreach ($delayTaskList as $delayTaskItem) {
            $delayTaskIds[] = $delayTaskItem['task_id'];
            switch ($delayTaskItem['type']) {
                case DelayOrderTaskModel::TYPE_TODAY_DONE:
                    $todayDoneTaskIds[] = $delayTaskItem['task_id'];
                    break;
                case DelayOrderTaskModel::TYPE_REQUEST_DELAY:
                case DelayOrderTaskModel::TYPE_WORK_ORDER:
                    $requestDelayTaskIds[] = $delayTaskItem['task_id'];
                    //判断延期时间--是否合理
                    if ($delayTaskItem['delay_time'] < time()) {
                        throw new LogicException('The delay time cannot be less than the current time', ErrorCode::DELAY_TIME_ERROR);
                    }
                    break;
            }
        }

        //如果上一次为申请延期待审批,这次为今日完成,需要将该任务从上次延期单中移除
        list($delayTaskMap, $projectId, $noReviewDelayOrderTaskList) = $this->getMaxDelayTaskList($delayTaskIds);

        //拿到今天时间 ,获取异常情况
        $todayStartTime = strtotime(date('Y-m-d'));
        $todayEndTime = strtotime(date('Y-m-d', strtotime('+1 day'))) - 1;

        //未审批的将不能在申请
        $todayDoneOrderTaskIds = [];
        $requestDelayOrderTaskIds = [];
        foreach ($noReviewDelayOrderTaskList as $noReviewDelayOrderTaskItem) {
            if ($this->isSelfDelay($noReviewDelayOrderTaskItem['type']) && in_array($noReviewDelayOrderTaskItem['task_id'], $todayDoneTaskIds)) {
                $todayDoneOrderTaskIds[] = $noReviewDelayOrderTaskItem['id'];
            } elseif (in_array($noReviewDelayOrderTaskItem['task_id'], $requestDelayTaskIds) || in_array($noReviewDelayOrderTaskItem['party_a_task_id'], $requestDelayTaskIds)) {
                $requestDelayOrderTaskIds[] = $noReviewDelayOrderTaskItem['id'];
            }
        }

        if (!empty($requestDelayOrderTaskIds)) {
            throw new LogicException("task already request delay", ErrorCode::TASK_ALREADY_REQUEST_DELAY);
        }

        $readyPublishStatus = StatusService::getInstance()->getTaskReadyToPublishStatus();
        $doneStatus = StatusService::getInstance()->getTaskDoneStatus();

        $delayOrderModel->startTrans();
        try {
            $delayOrder = $delayOrderModel->addItem([
                'number' => $number,
                'name' => $name,
                'episode_id' => $episodeId,
                'step_category_id' => $stepCategoryId,
                'project_id' => $projectId,
                'plan_delivery_time' => $plan['end_time'] ?? 0,
                'module_code' => $assetOrShot,
            ]);
            $needConfirmTaskIdList = [];
            $autoConfirmSelfStepCategoryAndCloseTaskIdList = [];
            $changeTaskPlanEndTimeRightNow = [];
            foreach ($delayTaskList as $delayTaskItem) {
                $delayTaskItem["delay_order_id"] = $delayOrder["id"];
                $delayTaskItem["created"] = time();
                $currentDelayTask = $delayTaskMap[$delayTaskItem['task_id']] ?? null;
                $delayTaskItem['abnormal_info'] = "";
                if (empty($currentDelayTask)) {
                    continue;
                }

                if (!isset($delayTaskItem["plan_delivery_time"])) {
                    $delayTaskItem["plan_delivery_time"] = $currentDelayTask['plan_delivery_time'];
                }
                if (!isset($delayTaskItem["original_plan_time"])) {
                    $delayTaskItem['original_plan_time'] = $currentDelayTask['plan_end_time'];
                }

                $delayTaskItem['project_id'] = $projectId;
                $delayTaskItem['task_status_id'] = $currentDelayTask['task_status'];
                $delayTaskItem['link_id'] = $reviewFeedbackId;
                //自身延期
                $selfDelay = $this->isSelfDelay($delayTaskItem['type']);
                //是否当前时间端内未完成
                if ($selfDelay) {
                    if ($currentDelayTask["plan_end_time"] >= $todayStartTime && $currentDelayTask["plan_end_time"] <= $todayEndTime &&
                        !in_array($currentDelayTask['task_status'], [$readyPublishStatus['id'], $doneStatus['id']])) {
                        $delayTaskItem['abnormal_info'] = "not_done";
                    } elseif ($delayTaskItem["plan_delivery_time"] >= $todayStartTime && $delayTaskItem["plan_delivery_time"] <= $todayEndTime && $currentDelayTask['task_status'] != $doneStatus['id']) { //是否是当前时间段内未交付
                        $delayTaskItem['abnormal_info'] = "not_delivery";
                    }
                }
                $delayTaskItem['uuid'] = create_uuid();
                $delayTaskItem['step_category_confirmed'] = 'default';
                $delayTaskItem['manager_confirmed'] = 'default';
                $delayTaskItem['manager_confirm_user_id'] = 0;
                $delayTaskItem['confirming_step_category_id'] = 0;
                $delayTaskItem['confirm_status'] = DelayOrderTaskModel::CONFIRM_STATUS_STEP_CATEGORY_CONFIRMING;
                // 只有申请延期 并且计划完成时间超出计划交付时间 才需要确认 否则直接通过
                if ($selfDelay
                    && (!empty($episode) && !empty($stepCategory))
                    && $currentDelayTask['plan_delivery_time'] < $delayTaskItem['delay_time']) {
                    $needConfirmTaskIdList[] = $delayTaskItem['task_id'];
                    $delayTaskItem['confirming_step_category_id'] = $delayOrder['step_category_id'];
                } else {
                    if ($selfDelay
                        && (empty($currentDelayTask['plan_delivery_time'])
                            || $currentDelayTask['plan_delivery_time'] >= $delayTaskItem['delay_time']
                        )) {
                        // 申请延期 没有超出计划交付时间 直接更新计划完成时间
                        // 没有计划交付时间的， 同样更新到计划完成时间
                        $changeTaskPlanEndTimeRightNow[] = [
                            'id' => $currentDelayTask['id'],
                            'plan_end_time' => $delayTaskItem['delay_time'],
                        ];
                    }
                    // 没有环节的生产任务,标准任务,自动延期通过
                    if ($selfDelay && (empty($stepCategory) || empty($episodeId)) && in_array($currentDelayTask['step_type'], TaskModel::STEP_TYPE_TODO_GROUP)) {
                        $changeTaskPlanEndTimeRightNow[] = [
                            'id' => $currentDelayTask['id'],
                            'plan_end_time' => $delayTaskItem['delay_time'],
                            'plan_delivery_time' => $delayTaskItem['delay_time'],
                        ];
                    }
                    // 自动通过其他类型
                    $delayTaskItem['step_category_confirmed'] = 'yes';
                    $delayTaskItem['manager_confirmed'] = 'yes';
                    $delayTaskItem['manager_confirm_user_id'] = fill_created_by();
                    $delayTaskItem['confirm_status'] = DelayOrderTaskModel::CONFIRM_STATUS_YES;
                    $autoConfirmSelfStepCategoryAndCloseTaskIdList[$delayTaskItem['task_id']] = $delayTaskItem;
                }
                $delayTaskItem["created_by"] = $createdBy;
                $delayTaskItem['step_category_id'] = $delayOrder['step_category_id'];
                $delayTaskItem['episode_id'] = $delayOrder['episode_id'];
                //今日完成
                if ($delayTaskItem['type'] == DelayOrderTaskModel::TYPE_TODAY_DONE) {
                    $delayTaskItem['delay_time'] = strtotime(date('Y-m-d') . ' 23:59:59');
                }
                $delayTaskData[] = $delayTaskItem;
            }
            model(DelayOrderTaskModel::class)->addAll($delayTaskData);
            $delayOrderTaskList = model(DelayOrderTaskModel::class)
                ->where([
                    'delay_order_id' => $delayOrder['id'],
                ])
                ->field('id, task_id')
                ->select();
            $delayOrder['delay_order_task_list'] = $delayOrderTaskList;
            //移除延期任务
            if (!empty($todayDoneOrderTaskIds)) {
                model(DelayOrderTaskModel::class)->deleteItem(['id' => ["IN", $todayDoneOrderTaskIds]]);
            }
            //如果没有需要延期的任务不需要发起审批
            if (!empty($delayOrderTaskList) && !empty($downstreamStepCategoryIdList)) {
                DelayOrderTaskStepCategoryConfirmService::getInstance()->createDelayOrderTaskStepCategoryConfirm(
                    $delayOrder, $delayOrderTaskList, $downstreamStepCategoryIdList, $delayTaskMap,
                    $needConfirmTaskIdList, $autoConfirmSelfStepCategoryAndCloseTaskIdList);
            }
            // 需要直接修改计划完成时间的延期单任务
            if (!empty($changeTaskPlanEndTimeRightNow)) {
                model(TaskModel::class)->saveAll($changeTaskPlanEndTimeRightNow);
            }
            if (empty($needConfirmTaskIdList)) {
                $delayOrderModel->save([
                    'id' => $delayOrder['id'],
                    'is_reviewed' => 'yes',
                    'confirm_status' => DelayOrderTaskModel::CONFIRM_STATUS_YES,
                ]);
            }
            $delayOrderModel->commit();
        } catch (Throwable $e) {
            $delayOrderModel->rollback();
            throw $e;
        }

        //todo 发送延期单确认通知
        if (!empty($needConfirmTaskIdList) && !empty($downstreamStepCategoryIdList)) {
            $hookData = [
                'type' => 'delay_order_confirm',
                'data' => [
                    'delay_order' => $delayOrder,
                    'creator_name' => $userName,
                    'step_category_id' => $downstreamStepCategoryIdList[0],
                ],
            ];
            Hook::listen('delay_order_created_after', $hookData);
        }
        if (!empty($delayOrder['id'])) {
            Client::send('delay_order_confirmed_check', build_queue_data_with_xu([
                'delay_order_id_list' => [$delayOrder['id']],
                'user_id' => request()->getUserId(),
            ]));
        }
        return $delayOrder;
    }

    /**
     * 判断是否自身延期
     * @param string $type 类型
     * @return int
     */
    private function isSelfDelay(string $type)
    {
        if (in_array($type, [
            DelayOrderTaskModel::TYPE_WORK_ORDER,
            DelayOrderTaskModel::TYPE_REQUEST_DELAY
        ])) {
            return 1;
        }
        return 0;
    }

    /**
     * 延期单列表
     * @param $param
     * @param int $userId
     * @return array
     */
    public function delayOrderList($param, int $userId)
    {
        $filter = $param['filter'];
        $page = $param['page'] ?? [1, C('default_page_total')];
        $order = $param['order'] ?? "delay_order.id desc";
        $waitForMeConfirm = $param['wait_for_me_confirm'] ?? 'no';
        $res = ['total' => 0, 'rows' => []];
        $fields = split_array($param['fields'] ?? []);

        $isExistTaskMember = false;
        $isExistDelayOrderTaskStepCategoryConfirm = false;
        $isEntityFilter = false;
        foreach ($filter as $filterIndex => $filterItem) {
            if (!$isExistTaskMember && strpos($filterIndex, "task_member.") !== false) {
                $isExistTaskMember = true;
            }
            if (!$isExistDelayOrderTaskStepCategoryConfirm && strpos($filterIndex, "delay_order_task_step_category_confirm.") !== false) {
                $isExistDelayOrderTaskStepCategoryConfirm = true;
            }
            if (!$isEntityFilter && strpos($filterIndex, "entity.") !== false) {
                $isEntityFilter = true;
            }
        }
        if ($waitForMeConfirm === 'yes') {
            $userCanConfirmStepCategoryIdList = ProjectStepCategoryConfirmConfigService::getInstance()
                ->getUserCanConfirmStepCategoryIdList(['GT', 0], $userId, 'delay');
            if (empty($userCanConfirmStepCategoryIdList)) {
                return $res;
            }
            $isExistDelayOrderTaskStepCategoryConfirm = true;
            $filter[] = [
                'delay_order_task_step_category_confirm.step_category_id' => ['IN', $userCanConfirmStepCategoryIdList],
            ];
        }

        if (empty($fields)) {
            $fields = [
                "delay_order.id",
                "delay_order.number",
                "delay_order.name",
                "delay_order.step_category_id",
                "delay_order.created_by",
                "delay_order.created",
                "delay_order.project_id",
                "delay_order.is_reviewed",
                "delay_order.confirm_status",
                "entity.code as episode_code",
                "project.name as project_name",
            ];
        }
        $delayOrderList = model(DelayOrderModel::class)
            ->join("delay_order_task on delay_order_task.delay_order_id = delay_order.id")
            ->join("entity on entity.id = delay_order.episode_id","left")
            ->join(("project on project.id = delay_order.project_id"));

        if ($isExistTaskMember) {
            $delayOrderList = $delayOrderList->join("task_member on task_member.task_id = delay_order_task.task_id");
        }

        if ($isExistDelayOrderTaskStepCategoryConfirm) {
            $delayOrderList = $delayOrderList->join("delay_order_task_step_category_confirm " .
                "on delay_order_task_step_category_confirm.delay_order_task_id = delay_order_task.id");
        }

        $delayOrderList = $delayOrderList->where($filter)
            ->page(...$page)
            ->field($fields)
            ->group('delay_order.id')
            ->order($order)
            ->select();
        if (empty($delayOrderList)) {
            return $res;
        }
        $delayOrderCount = model(DelayOrderModel::class)
            ->join("delay_order_task on delay_order_task.delay_order_id = delay_order.id");

        if ($isExistTaskMember) {
            $delayOrderCount = $delayOrderCount->join("task_member on task_member.task_id = delay_order_task.task_id");
        }
        if ($isEntityFilter) {
            $delayOrderCount = $delayOrderCount->join("entity on entity.id = delay_order.episode_id");
        }
        if ($isExistDelayOrderTaskStepCategoryConfirm) {
            $delayOrderCount = $delayOrderCount->join("delay_order_task_step_category_confirm " .
                "on delay_order_task_step_category_confirm.delay_order_task_id = delay_order_task.id");
        }

        $delayOrderCount = $delayOrderCount->where($filter)
            ->field('count(distinct delay_order.id) as count')
            ->find();
        $delayOrderCount = $delayOrderCount['count'] ?? 0;
        if (count($fields) == 1 && in_array("delay_order.id", $fields)) {
            $res = [
                'count' => $delayOrderCount,
                'rows' => $delayOrderList
            ];
            return $res;
        }
        $userIds = [];
        $stepCategoryIds = [];
        foreach ($delayOrderList as $delayOrderItem) {
            $userIds[$delayOrderItem['created_by']] = $delayOrderItem['created_by'];
            $stepCategoryIds[$delayOrderItem['step_category_id']] = $delayOrderItem['step_category_id'];
        }

        $stepCategoryList = model(StepCategoryModel::class)->where(["id" => ["IN", join(",", $stepCategoryIds)]])->field("id,name")->select();
        $stepCategoryMap = array_column($stepCategoryList, null, 'id');

        $userList = model(UserModel::class)->where(["id" => ["IN", join(",", $userIds)]])->field("id,name")->select();
        $userMap = array_column($userList, null, 'id');

        $sortIndex = 0;
        $delayOrderList = array_map(function ($delayOrderItem) use ($userMap, $stepCategoryMap, &$sortIndex) {
            $delayOrderItem['created_by'] = $userMap[$delayOrderItem['created_by']] ?? null;
            $delayOrderItem['step_category'] = $stepCategoryMap[$delayOrderItem['step_category_id']] ?? null;
            $sortIndex += 1;
            $delayOrderItem['sort_index'] = $sortIndex;
            return $delayOrderItem;
        }, $delayOrderList);

        return ['total' => $delayOrderCount, 'rows' => $delayOrderList];
    }

    /**
     * 延期单详情
     * @param $delayOrderId
     * @return array
     */
    public function delayOrderDetail($delayOrderId)
    {
        $delayOrderTaskList = model(DelayOrderTaskModel::class)
            ->where([
                'delay_order_id' => $delayOrderId,
            ])
            ->field("id,delay_order_id,task_id,original_plan_time,delay_time,delay_reason,created_by,created")
            ->select();

        if (empty($delayOrderTaskList)) {
            return [];
        }

        $delayOrder = model(DelayOrderModel::class)->field("project_id,step_category_id,episode_id,created_by,created")->find($delayOrderId);

        $delayOrder['created_by'] = model(UserModel::class)->field("id,name")->find($delayOrder['created_by']);

        $plan = model(PlanModel::class)
            ->join("task_relation_tag s_tag on s_tag.task_id = plan.task_id and s_tag.link_module_code in ('step_category','show_step_category')")
            ->where([
                'plan.project_id' => $delayOrder['project_id'],
                'plan.type' => PlanModel::TYPE_STEP_CATEGORY,
                'plan.scope' => PlanModel::SCOPE_SINGLE,
                'plan.entity_id' => $delayOrder['episode_id'],
                's_tag.link_id' => $delayOrder['step_category_id'],
            ])
            ->field("plan.id,plan.end_time")
            ->find();

        $taskIds = [];
        foreach ($delayOrderTaskList as $delayOrderTaskItem) {
            $taskIds[$delayOrderTaskItem['task_id']] = $delayOrderTaskItem['task_id'];
        }

        $fields = [
            "task.id", "task.name as task_name", "task.project_id", "task.step_type", "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_delay_sum')) as task_delay_sum", 'task.episode_id', 'task.step_category_id',
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as status", "task.plan_end_time", 'task.assign_status',
            "entity.initial_episode_code", "entity.showings_number", "entity.name",
        ];

        $taskList = model(TaskModel::class)
            ->join("entity on entity.id = task.entity_id", "LEFT")
            ->where(["task.id" => ["IN", $taskIds]])
            ->field($fields)
            ->select();

        if (!empty($taskList)) {
            ProducerManageService::getInstance()->getTaskAbnormalDetail($taskList);
        }

        $taskMap = [];
        foreach ($taskList as $taskItem) {
            $taskItem['task_delay_sum'] = $taskItem['task_delay_sum'] ?? 0;
            $taskMap[$taskItem['id']] = $taskItem;
        }

        foreach ($delayOrderTaskList as $delayOrderTaskIndex => $delayOrderTaskItem) {
            $delayOrderTaskItem['task'] = $taskMap[$delayOrderTaskItem['task_id']] ?? [];

            $delayOrderTaskList[$delayOrderTaskIndex] = $delayOrderTaskItem;
        }
        return array_merge($delayOrder, ['plan' => $plan, 'children' => $delayOrderTaskList]);
    }

    /**
     * 延期任务列表
     * @return array
     */
    public function delayTaskList($param)
    {
        $filter = $param['filter'];
        $type = $param['type'] ?? 'delay_delivery';
        $page = $param['page'] ?? [1, C('default_page_total')];

        if ($type == 'delay_delivery') {
            $filter[] = [
                'review_request.is_reviewed' => 'yes',
                'review_request.is_pass' => 'yes',
            ];
        }

        $delayOrderTask = model(DelayOrderModel::class)
            ->join("delay_order_task on delay_order_task.delay_order_id = delay_order.id")
            ->join("review_request on review_request.link_id = delay_order_task.id and review_request.module_id =" . module_by_code("delay_order_task")['id'])
            ->where($filter)
            ->page(...$page)
            ->field("delay_order_task.task_id,max(original_plan_time) as original_plan_time,max(delay_time) as delay_time")
            ->group("delay_order_task.task_id")
            ->select();

        if (empty($delayOrderTask)) {
            return [];
        }

        $taskIds = array_column($delayOrderTask, 'task_id');

        $taskList = model(TaskModel::class)
            ->join("entity on entity.id = task.entity_id")
            ->where(["task.id" => ["IN", $taskIds]])
            ->field("task.id,entity.initial_episode_code,entity.showings_number,entity.name,task.name as task_name,task.step_category_id")
            ->select();

        $taskMap = array_column($taskList, null, 'id');
        $stepCategoryIds = array_unique(array_column($taskList, 'step_category_id'));
        $stepCategoryList = model(StepCategoryModel::class)->where(['id' => ["IN", $stepCategoryIds]])->field("id,name,code")->select();
        $stepCategoryMap = array_column($stepCategoryList, null, 'id');

        foreach ($delayOrderTask as &$delayOrderTaskItem) {
            $taskMap[$delayOrderTaskItem['task_id']]['step_category'] = $stepCategoryMap[$taskMap[$delayOrderTaskItem['task_id']]['step_category_id']] ?? [];
            $delayOrderTaskItem['task'] = $taskMap[$delayOrderTaskItem['task_id']];
        }
        return $delayOrderTask;
    }

    /**
     * 批量创建延期单
     * @param $data
     * @param $createdBy
     * @return array
     * @throws Throwable
     */
    public function batchCreateDelayOrder($data, $createdBy)
    {
        $result = [];
        foreach ($data as $dataItem) {
            $result[] = $this->createDelayOrder($dataItem, $createdBy);
        }
        return $result;
    }

    /**
     * 获取环节下的延期原因标签
     * @param $stepCategoryId
     * @return array
     */
    public function getStepCategoryDelayReason($stepCategoryId)
    {
        $optionsList = OptionsService::getInstance()->getOptionsData("delay_reason_tag");
        $delayReasonTagMap = [];
        foreach ($optionsList as $reasonStepCategory => $reasonStr) {
            $delayReasonTagMap[$reasonStepCategory] = explode(",", $reasonStr);
        }

        $result = [];
        $stepCategoryId = explode(",", $stepCategoryId);
        foreach ($stepCategoryId as $stepCategoryIdItem) {
            $result = array_merge($result, $delayReasonTagMap[$stepCategoryIdItem] ?? []);
        }
        return $result;
    }

    /**
     * 获取时间范围内最大延期任务列表
     * @param $stepCategoryId
     * @param $departmentId
     * @param $departmentUserIds
     * @param $projectId
     * @param $timeScope
     * @return array|false|mixed|string
     */
    public function getTodoDayMaxDelayOrderTaskList($stepCategoryId, $departmentId, $departmentUserIds, $projectId, $timeScope, $taskIds)
    {

        $delayOrderTaskIds = [];
        if (!empty($stepCategoryId)) {
            $delayOrderTaskIds = model(DelayOrderTaskModel::class)
                ->join("delay_order on delay_order.id = delay_order_task.delay_order_id")
                ->where([
                    'delay_order.project_id' => $projectId,
                    'delay_order.step_category_id' => ["IN", $stepCategoryId],
                    [
                        ['delay_order_task.original_plan_time' => $timeScope, [
                            ['delay_order_task.type' => 'request_delay', 'delay_order_task.abnormal_info' => DelayOrderTaskModel::ABNORMAL_INFO_NOT_DONE],
                            ['delay_order_task.type' => 'today_done'],
                            ['delay_order_task.type' => 'request_delay', 'delay_order_task.abnormal_info' => ''],
                            '_logic' => "OR",
                        ]],
                        ['delay_order_task.plan_delivery_time' => $timeScope, [
                            ['delay_order_task.type' => 'request_delay', 'delay_order_task.abnormal_info' => DelayOrderTaskModel::ABNORMAL_INFO_NOT_DELIVERY],
                            ['delay_order_task.type' => 'today_done'],
                            '_logic' => "OR",
                        ]],
                        '_logic' => "OR",
                    ],
                ])
                ->field("max(delay_order_task.id) as id")
                ->group("task_id")
                ->column('id');
        } elseif ($taskIds) {
            if (empty($taskIds[1])) {
                return [];
            }
            $taskIds = explode(',', $taskIds[1]);
            $delayOrderTaskIds = model(DelayOrderTaskModel::class)
                ->where([
                    'project_id' => $projectId,
                    'task_id' => ['IN', $taskIds],
                    [
                        ['original_plan_time' => $timeScope, [
                            ['type' => 'request_delay', 'abnormal_info' => DelayOrderTaskModel::ABNORMAL_INFO_NOT_DONE],
                            ['type' => 'today_done'],
                            '_logic' => "OR",
                        ]],
                        ['plan_delivery_time' => $timeScope, [
                            ['type' => 'request_delay', 'abnormal_info' => DelayOrderTaskModel::ABNORMAL_INFO_NOT_DELIVERY],
                            ['type' => 'today_done'],
                            '_logic' => "OR",
                        ]],
                        '_logic' => "OR",
                    ],
                ])
                ->field("max(id) as id")
                ->group("task_id")
                ->column('id');
        } elseif (!empty($departmentId)) {
            $delayOrderTaskIds = model(DelayOrderTaskModel::class)
                ->join("task_member on task_member.task_id = delay_order_task.task_id")
                ->where([
                    'delay_order_task.project_id' => $projectId,
                    'task_member.partner' => ['IN', $departmentUserIds],
                    [
                        ['delay_order_task.original_plan_time' => $timeScope, [
                            ['delay_order_task.type' => 'request_delay', 'delay_order_task.abnormal_info' => DelayOrderTaskModel::ABNORMAL_INFO_NOT_DONE],
                            ['delay_order_task.type' => 'today_done'],
                            '_logic' => "OR",
                        ]],
                        ['delay_order_task.plan_delivery_time' => $timeScope, [
                            ['delay_order_task.type' => 'request_delay', 'delay_order_task.abnormal_info' => DelayOrderTaskModel::ABNORMAL_INFO_NOT_DELIVERY],
                            ['delay_order_task.type' => 'today_done'],
                            '_logic' => "OR",
                        ]],
                        '_logic' => "OR",
                    ],
                ])
                ->field("max(delay_order_task.id) as id")
                ->group("delay_order_task.task_id")
                ->column('id');
        }

        if (empty($delayOrderTaskIds)) {
            return [];
        }

        return model(DelayOrderTaskModel::class)->where([
            'id' => ["IN", $delayOrderTaskIds],
        ])->select();
    }

    /**
     * 延期单审核详情
     * @param $param
     * @param $userId
     * @return array
     * @throws \Exception
     */
    public function delayOrderReviewDetail($param, $userId)
    {
        $isCurrentReview = isset($param['is_current_review']) && $param['is_current_review'] == "yes";
        $filter = $param['filter'];
        $delayOrderId = $filter['delay_order_task.delay_order_id'] ?? $filter['delay_order_id'];
        if (isset($filter['delay_order_id'])) {
            $filter['delay_order_task.delay_order_id'] = $filter['delay_order_id'];
            unset($filter['delay_order_id']);
        }
        $delayOrderTaskFilter = [];
        $reviewRequestFilter = [];
        foreach ($filter as $filterIndex => $filterItem) {
            if (strpos($filterIndex, "delay_order_task.") !== false || strpos($filterIndex, "task_member.") !== false) {
                $delayOrderTaskFilter[$filterIndex] = $filterItem;
            }

            if (strpos($filterIndex, "review_request.") !== false || strpos($filterIndex, "review_request_node.") !== false) {
                $reviewRequestFilter[$filterIndex] = $filterItem;
            }
        }
        $delayOrderTaskFilter['delay_order_task.type'] = ['in', [DelayOrderTaskModel::TYPE_REQUEST_DELAY, DelayOrderTaskModel::TYPE_WORK_ORDER]];

        $delayOrderFields = [
            "delay_order_task.id","delay_order_task.delay_order_id","delay_order_task.task_id",
            "delay_order_task.original_plan_time","delay_order_task.plan_delivery_time",
            "delay_order_task.delay_time","delay_order_task.delay_reason",
            "delay_order_task.created_by","delay_order_task.created",
        ];
        $delayOrderTaskList = model(DelayOrderTaskModel::class)
            ->join("task_member on task_member.task_id = delay_order_task.task_id", "LEFT")
            ->where($delayOrderTaskFilter)
            ->field($delayOrderFields)
            ->group("delay_order_task.id")
            ->select();

        if (empty($delayOrderTaskList)) {
            return [];
        }

        //是否是当前用户审核
        if ($isCurrentReview) {
            $reviewRequestFilter = array_merge([
                "review_request_node.plan_reviewer" => $userId,
                "review_request.is_reviewed" => "no",
                "review_request_node.is_active" => "yes",
            ], $reviewRequestFilter);
        }

        //获取审核列表
        $reviewRequestNodeList = model(ReviewRequestModel::class)
            ->join("review_request_node on review_request_node.review_request_id = review_request.id")
            ->where($reviewRequestFilter)
            ->where([
                'review_request.link_id' => ["IN", array_column($delayOrderTaskList, 'id')],
                'review_request.module_id' => module_by_code('delay_order_task')['id'],
            ])
            ->field([
                'review_request_node.id', 'review_request_node.review_request_id', 'review_request.link_id', 'review_request_node.is_active', 'review_request_node.plan_reviewer',
            ])
            ->select();

        $isReviewDelayOrderTaskIds = [];
        $reviewRequestIdList = [];
        $delayOrderReviewRequestMap = [];
        $isCurrentUserReviewMap = [];
        foreach ($reviewRequestNodeList as $reviewRequestNodeItem) {
            $reviewRequestIdList[$reviewRequestNodeItem['review_request_id']] = $reviewRequestNodeItem['review_request_id'];
            $isReviewDelayOrderTaskIds[$reviewRequestNodeItem['link_id']] = $reviewRequestNodeItem['link_id'];
            $delayOrderReviewRequestMap[$reviewRequestNodeItem['link_id']] = $reviewRequestNodeItem['review_request_id'];
            if ($reviewRequestNodeItem['is_active'] == "yes" && $reviewRequestNodeItem['plan_reviewer'] == $userId) {
                $isCurrentUserReviewMap[$reviewRequestNodeItem['review_request_id']] = $reviewRequestNodeItem['id'];
            }
        }
        //审批进度
        $progressData = ReviewRequestService::getInstance()->queryReviewRequestProgressData($reviewRequestIdList);

        $delayOrder = model(DelayOrderModel::class)
            ->field("project_id,name,step_category_id,episode_id,created_by,created")
            ->find($delayOrderId);
        $delayOrder['created_by'] = model(UserModel::class)
            ->field("id,name")
            ->find($delayOrder['created_by']);
        $taskIds = [];
        foreach ($delayOrderTaskList as $delayOrderTaskIndex => $delayOrderTaskItem) {
            if (!isset($isReviewDelayOrderTaskIds[$delayOrderTaskItem['id']])) {
                unset($delayOrderTaskList[$delayOrderTaskIndex]);
                continue;
            }
            $taskIds[$delayOrderTaskItem['task_id']] = $delayOrderTaskItem['task_id'];
        }

        $fields = [
            "task.id","task.name as task_name","task.project_id","task.step_type",
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_delay_sum')) as task_delay_sum",
            'task.episode_id','task.step_category_id',
            "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as status",
            "task.plan_end_time",'task.assign_status','task.executor',
            "entity.initial_episode_code","entity.showings_number","entity.name",
        ];

        $taskList = [];
        $userList = [];
        $taskMemberList = [];
        if (!empty($taskIds)) {
            $taskList = model(TaskModel::class)
                ->join("entity on entity.id = task.entity_id", "LEFT")
                ->where(["task.id" => ["IN", $taskIds]])
                ->field($fields)
                ->select();

            ProducerManageService::getInstance()->getTaskAbnormalDetail($taskList);

            $userIdList = [];
            $todoTaskIds = [];
            foreach ($taskList as $taskItem) {
                if ($taskItem['step_type'] == TaskModel::STEP_TYPE_STEP) {
                    $userIdList[$taskItem['executor']] = $taskItem['executor'];
                } else {
                    $todoTaskIds[] = $taskItem['id'];
                }
            }

            if (!empty($todoTaskIds)) {
                $taskMemberList = model(TaskMemberModel::class)
                    ->where(['task_id' => ['IN', implode(',', $todoTaskIds)]])
                    ->where(['partner' => ['GT', 0]])
                    ->select();
                foreach ($taskMemberList as $taskMember) {
                    if ($taskMember['user_id'] > 0) {
                        $userIdList[$taskMember['user_id']] = $taskMember['user_id'];
                    }
                    if ($taskMember['partner'] > 0) {
                        $userIdList[$taskMember['partner']] = $taskMember['partner'];
                    }
                }
                $taskMemberList = array_group_by($taskMemberList, 'task_id');
            }

            //获取执行人名
            if (!empty($userIdList)) {
                $executorList = model(UserModel::class)
                    ->where(['id' => ["IN", array_values($userIdList)]])
                    ->field("id,name,avatar")
                    ->select();
                $userList = array_column($executorList, null, 'id');
            }
        }

        $taskMap = [];
        foreach ($taskList as $taskItem) {
            $taskItem['task_delay_sum'] = $taskItem['task_delay_sum'] ?? 0;
            if ($taskItem['step_type'] == TaskModel::STEP_TYPE_STEP) {
                $taskItem['task_member_list'] = [[
                    'id' => $taskItem['id'],
                    'status' => $taskItem['status'] != 7 ? 1 : 7,
                    'user_info' => $userList[$taskItem['executor']] ?? null,
                    'type' => 'executor',
                ]];
            } else {
                $taskItem['task_member_list'] = $taskMemberList[$taskItem['id']] ?? [];
                foreach ($taskItem['task_member_list'] as &$taskMember) {
                    if ($taskMember['partner'] > 0) {
                        $taskMember['user_info'] = $userList[$taskMember['partner']] ?? null;
                    }
                    $taskMember['type'] = $taskMember['user_id'] > 0 ? 'executor' : 'partner';
                }
            }
            $taskMap[$taskItem['id']] = $taskItem;
        }

        foreach ($delayOrderTaskList as $delayOrderTaskIndex => $delayOrderTaskItem) {
            $delayOrderTaskItem['task'] = $taskMap[$delayOrderTaskItem['task_id']] ?? [];
            $currentReviewRequestId = $delayOrderReviewRequestMap[$delayOrderTaskItem['id']];
            $delayOrderTaskItem['review_progress'] = $progressData[$currentReviewRequestId] ?? null;
            $delayOrderTaskItem['module_id'] = module_by_code("delay_order_task")['id'];

            //判定当前用户是否能审核
            $delayOrderTaskItem['review_request_node_id'] = 0;
            $delayOrderTaskItem['is_can_review'] = false;
            if (isset($isCurrentUserReviewMap[$currentReviewRequestId])) {
                $delayOrderTaskItem['is_can_review'] = true;
                $delayOrderTaskItem['review_request_node_id'] = $isCurrentUserReviewMap[$currentReviewRequestId];
            }

            $delayOrderTaskList[$delayOrderTaskIndex] = $delayOrderTaskItem;
        }
        return array_merge($delayOrder, ['children' => array_values($delayOrderTaskList)]);
    }

    /**
     * 受影响任务查询
     * @param $delayOrderId
     * @return array|false|mixed|string
     * @throws Exception
     */
    public function effectedTaskList($delayOrderId)
    {
        /**
         * 1. 查询所有工序的下游工序
         * 2. 查询所有任务的实体下游实体信息
         * 3. 查询出所有的受影响任务 带时间筛选
         * 4. 进行信息归并 统计下游任务信息
         */
        $delayOrder = model(DelayOrderModel::class)->find($delayOrderId);
        $project = ProjectService::getInstance()->getProject($delayOrder['project_id'], '*');

        $module = StepCategoryService::getInstance()->getStepCategoryTopModule($delayOrder['step_category_id']);
        $delayOrderTaskList = model(DelayOrderTaskModel::class)->where(['delay_order_id' => $delayOrderId])->select();

        $taskList = model(TaskModel::class)->where(['id' => ['IN', array_column($delayOrderTaskList, 'task_id')]])->field('id,step_id,entity_id')->select();
        $stepIdList = array_unique(array_column($taskList, 'step_id'));
        $entityIdList = array_unique(array_column($taskList, 'entity_id'));

        $res = [];
        if (empty($stepIdList) || empty($entityIdList)) {
            return $res;
        }

        // 工序下游工序
        $stepDownstream = StepService::getInstance()->getUpstreamProcessBatch($project['project_template_id'], $stepIdList, $module['code'], true, true, 'down');
        // 实体下游实体信息
        $downstreamEntityList = EntityDownstreamService::getInstance()->getDownstreamEntityListBatch($entityIdList, true);
        $taskDelayTimeMap = array_column($delayOrderTaskList, 'delay_time', 'task_id');
        $assetFilter = [];
        // 组装查询条件和索引关系
        foreach ($taskList as &$task) {
            $delayToTime = $taskDelayTimeMap[$task['id']];
            $task['step_downstream'] = [];
            if (empty($stepDownstream[$task['step_id']]) || empty($stepDownstream[$task['step_id']]['step_list'])) {
                continue;
            }
            $task['step_downstream'] = $stepDownstream[$task['step_id']]['step_list'];
            $task['entity_downstream'] = [];
            if (empty($downstreamEntityList[$task['entity_id']])) {
                continue;
            }
            $task['entity_downstream'] = $downstreamEntityList[$task['entity_id']]['downstream_entity_list'];
            $task['entity_downstream'][$task['entity_id']] = $downstreamEntityList[$task['entity_id']];
            foreach ($task['step_downstream'] as $taskStepDownstream) {
                if ($taskStepDownstream['step_id'] === $task['step_id']) {
                    continue;
                }
                $taskStepDownstreamModuleCode = $taskStepDownstream['module_code'];
                foreach ($task['entity_downstream'] as $taskEntityDownstream) {
                    $entityModule = module_by_id($taskEntityDownstream['module_id']);
                    if ($taskStepDownstreamModuleCode !== $entityModule['code']) {
                        continue;
                    }
                    $task['downstream_task_relation_key'][] = $taskEntityDownstream['id'] . '#' . $taskStepDownstream['step_id'];
                    $assetFilter[] = [
                        'entity.id' => $taskEntityDownstream['id'],
                        'task.step_id' => $taskStepDownstream['step_id'],
                        'task.plan_start_time' => ['BETWEEN', [1, $delayToTime]],
                    ];
                }
            }
            unset($task['step_downstream'], $task['entity_downstream']);
        }
        if (empty($assetFilter)) {
            return $res;
        }
        if (count($assetFilter) > 1) {
            $assetFilter['_logic'] = 'OR';
        }
        // 查询所有受影响的下游任务列表
        $downstreamTaskFields = [
            'entity.id',
            'entity.initial_episode_code',
            'entity.showings_number',
            'entity.name',
            'entity.module_id',
            'entity.json',
            'task.id',
            'task.name',
            'task.step_id',
            'task.plan_start_time',
            'task.plan_end_time',
            'task.start_time',
            'task.end_time',
            'task.plan_delivery_time',
            'task.delivery_time',
            'task.json',
            'step.id',
            'step.name',
            'step_category.id',
            'step_category.name'
        ];
        $downstreamTaskList = model(EntityModel::class)
            ->join('task on task.entity_id = entity.id')
            ->join('step on step.id = task.step_id')
            ->join('step_category on step_category.id = task.step_category_id')
            ->where($assetFilter)
            ->field(build_complex_query_fields($downstreamTaskFields))
            ->select();
        // 组装唯一键
        $episodeCodeList = [];
        foreach ($downstreamTaskList as &$downstreamTaskItem) {
            $downstreamTaskItem = separate_complex_query_data($downstreamTaskItem);
            $downstreamTaskItem['uk'] = $downstreamTaskItem['entity']['id'] . '#' . $downstreamTaskItem['task']['step_id'];
            if (!empty($downstreamTaskItem['entity']['initial_episode_code']) && !in_array($downstreamTaskItem['entity']['initial_episode_code'], $episodeCodeList)) {
                $episodeCodeList[] = $downstreamTaskItem['entity']['initial_episode_code'];
            }
            $downstreamTaskItem['entity']['json'] = json_decode($downstreamTaskItem['entity']['json'], true);
            $downstreamTaskItem['entity'] = array_merge($downstreamTaskItem['entity'], $downstreamTaskItem['entity']['json']);
            $downstreamTaskItem['task']['json'] = json_decode($downstreamTaskItem['task']['json'], true);
            $downstreamTaskItem['task'] = array_merge($downstreamTaskItem['task'], $downstreamTaskItem['task']['json']);
            unset($downstreamTaskItem['task']['json'], $downstreamTaskItem['entity']['json']);
        }

        $episodeList = [];
        if (!empty($episodeCodeList)) {
            $episodeList = EpisodeService::getInstance()->selectEpisodeMap(['code' => ['IN', $episodeCodeList]], 'id,name,code,json');
        }
        $episodeAnimatePlan = [];
        if (!empty($episodeList)) {
            // 查询动画制作开始时间
            $sc = ProjectService::getInstance()->getShowStepCategoryGroup();
            $sc = array_column($sc, 'children_ids', 'name');
            if (!empty($sc['动画'])) {
                $episodeAnimatePlan = model(PlanModel::class)
                    ->where(['plan.type' => PlanModel::TYPE_STEP_CATEGORY, 'plan.scope' => PlanModel::SCOPE_SINGLE])
                    ->join("task_relation_tag r_step_category on r_step_category.task_id = plan.task_id and r_step_category.link_module_code in ('step_category','show_step_category')")
                    ->where(['plan.entity_id' => ['IN', array_column($episodeList, 'id')], 'r_step_category.link_id' => ['IN', $sc['动画']]])
                    ->field("plan.entity_id episode_id,plan.start_time,plan.end_time")
                    ->group('plan.entity_id')
                    ->select();
                $episodeAnimatePlan = array_column($episodeAnimatePlan, null, 'episode_id');
            }
            $statusList = StatusService::getInstance()->getList([], 'id,name,code,color');
            $statusList = array_column($statusList, null, 'id');
            foreach ($episodeList as &$episode) {
                $episode['animate_plan'] = $episodeAnimatePlan[$episode['id']] ?? null;
                $episodeStatus = $episode['episode_status'] ?? 0;
                $episode['json'] = json_decode($episode['json'], true);
                $episode = array_merge($episode, $episode['json']);
                $episode['episode_status'] = $statusList[$episodeStatus] ?? null;
                unset($episode['json']);
            }
        }
        $downstreamTaskList = array_column($downstreamTaskList, null, 'uk');

        // 根据延期单的所属环节 进行数据计算
        switch ($module['code']) {
            case 'design':
            case 'asset':
            case 'level':
                // 任务 + 剧集
                unset($task);
                foreach ($taskList as &$task) {
                    $task['downstream_res'] = [
                        'task' => [
                            'data' => [],
                            'count' => 0,
                        ],
                        'task_group_by_episode' => [
                            'data' => [],
                            'count' => 0,
                        ],
                    ];
                    $eMap = [];
                    $ei = 0;
                    $eEntityIdList = [];
                    unset($task['entity_downstream'], $task['step_downstream']);
                    foreach ($task['downstream_task_relation_key'] ?? [] as $uk) {
                        if (!isset($downstreamTaskList[$uk])) {
                            continue;
                        }
                        $downstreamTask =  &$downstreamTaskList[$uk];
                        switch (module_by_id($downstreamTask['entity']['module_id'])['code']) {
                            case 'design':
                            case 'asset':
                            case 'level':
                                $task['downstream_res']['task']['data'][] = $downstreamTask;
                                break;
                            case 'sequence':
                            case 'shot':
                                if (in_array($downstreamTask['entity']['id'], $eEntityIdList)) {
                                    break;
                                }
                                $eEntityIdList[] = $downstreamTask['entity']['id'];
                                $taskEpisodeCode = $downstreamTask['entity']['initial_episode_code'];
                                if (!isset($episodeList[$taskEpisodeCode])) {
                                    break;
                                }
                                if (!isset($eMap[$taskEpisodeCode])) {
                                    $task['downstream_res']['task_group_by_episode']['data'][] = $episodeList[$taskEpisodeCode];
                                    $eMap[$taskEpisodeCode] = $ei;
                                    $ei += 1;
                                }
                                $taskEpisodeIndex = $eMap[$taskEpisodeCode];
                                $task['downstream_res']['task_group_by_episode']['data'][$taskEpisodeIndex]['children'][] = $downstreamTask['entity'];
                                break;
                        }
                    }
                    $task['downstream_res']['task']['count'] = count($task['downstream_res']['task']['data']);
                    $task['downstream_res']['task_group_by_episode']['count'] = count($task['downstream_res']['task_group_by_episode']['data']);
                    unset($task['downstream_task_relation_key']);
                }
                break;
            case 'sequence':
            case 'shot':
            default:
                // 任务
                unset($task);
                foreach ($taskList as &$task) {
                    $task['downstream_res'] = [
                        'task' => [
                            'data' => [],
                            'count' => 0,
                        ],
                        'task_group_by_episode' => [
                            'data' => [],
                            'count' => 0,
                        ],
                    ];
                    foreach ($task['downstream_task_relation_key'] ?? [] as $uk) {
                        if (!isset($downstreamTaskList[$uk])) {
                            continue;
                        }
                        $downstreamTask =  &$downstreamTaskList[$uk];
                        $task['downstream_res']['task']['data'][] = $downstreamTask;
                    }
                    $task['downstream_res']['task']['count'] = count($task['downstream_res']['task']['data']);
                    $task['downstream_res']['task_group_by_episode']['count'] = count($task['downstream_res']['task_group_by_episode']['data']);
                    unset($task['downstream_task_relation_key']);
                }
                break;
        }
        return $taskList;
    }

    /**
     * 获取延期任务审核进度
     * @return array
     * @throws \Exception
     */
    public function findDelayOrderTaskReviewProgress($taskId)
    {
        //拿到延期任务最新的审核进度
        $reviewRequest = model(ReviewRequestModel::class)
            ->join("delay_order_task on delay_order_task.id = review_request.link_id and review_request.module_id = " . module_by_code("delay_order_task")['id'])
            ->where([
                'delay_order_task.task_id' => $taskId,
                'review_request.is_reviewed' => "no",
            ])
            ->field("review_request.id")
            ->order("review_request.id desc")
            ->find();

        if (empty($reviewRequest)) {
            return [];
        }

        $reviewProgress = ReviewRequestService::getInstance()->queryReviewRequestProgressData([$reviewRequest['id']]);
        return $reviewProgress[$reviewRequest['id']] ?? [];
    }

    /**
     * 获取最大的延期任务列表
     * @param $delayTaskIds
     * @return array
     */
    private function getMaxDelayTaskList($delayTaskIds)
    {
        $delayTaskPartyAMap = OrderService::getInstance()->getSourceTaskListByTaskIdList($delayTaskIds, true);
        $delayTaskArray = model(TaskModel::class)
            ->where(["id" => ["IN", array_merge($delayTaskIds, array_values($delayTaskPartyAMap))]])
            ->field(["id", "project_id", "step_type", "plan_end_time", "plan_delivery_time", "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status')) as task_status", 'step_category_id'])
            ->select();
        //如果上一次为申请延期待审批,这次为今日完成,需要将该任务从上次延期单中移除

        $delayTaskMap = [];
        $projectId = 0;
        $delayTaskFilter = [];
        foreach ($delayTaskArray as $delayTask) {
            if (empty($projectId)) {
                $projectId = $delayTask['project_id'];
            }
            $delayTaskMap[$delayTask['id']] = $delayTask;
            $delayTaskFilter[] = [
                'delay_order_task.task_id' => $delayTask['id'],
                [
                    'delay_order_task.plan_delivery_time' => $delayTask['plan_delivery_time'],
                    'delay_order_task.original_plan_time' => $delayTask['plan_end_time'],
                    "_logic" => "OR",
                ],
            ];
        }

        if (!empty($delayTaskFilter)) {
            $delayTaskFilter["_logic"] = "OR";
            $delayTaskFilter = [$delayTaskFilter];
        }

        // 获取到该时间段内 最后一个
        $maxDelayOrderTaskIds = model(DelayOrderTaskModel::class)
//            ->join("review_request on review_request.link_id = delay_order_task.id and review_request.module_id =" . module_by_code("delay_order_task")['id'])
//            ->where(["review_request.is_reviewed" => "no", $delayTaskFilter])
            ->where(["delay_order_task.confirm_status" => ['not in', ['yes', 'no']], $delayTaskFilter])
            ->field("max(delay_order_task.id) as id")
            ->group("delay_order_task.task_id")
            ->column("id");

        $noReviewDelayOrderTaskList = [];
        if (!empty($maxDelayOrderTaskIds)) {
            $noReviewDelayOrderTaskList = model(DelayOrderTaskModel::class)
                ->where(["delay_order_task.id" => ["IN", $maxDelayOrderTaskIds]])
                ->field("delay_order_task.id,delay_order_task.task_id,delay_order_task.type,delay_order_task.abnormal_info")
                ->select();
            foreach ($noReviewDelayOrderTaskList as &$item) {
                $item['party_a_task_id'] = $delayTaskPartyAMap[$item['task_id']] ?? $item['task_id'];
            }
        }

        return [$delayTaskMap, $projectId, $noReviewDelayOrderTaskList];

    }

    /**
     * 延期环节列表
     * @param $projectId
     * @param $stepCategoryId
     * @return array
     */
    public function delayModuleStepCategoryList($projectId, $stepCategoryId)
    {
        //查询环节的工序分类数据信息
        $stepCategory = StepCategoryService::getInstance()->getOne(['id' => $stepCategoryId], 'id,parent_id');
        //工序分类的上级不存在 提示环节无效
        if (empty($stepCategory['parent_id'])) {
            throw new LogicException('stepCategory data is invalid', ErrorCode::DATA_INVALID);
        }
        //查询工序分类的父级的实体entity_id,和工序分类id
        $parentStepCategory = StepCategoryService::getInstance()->getOne(['id' => $stepCategory['parent_id']], 'id,entity_id');
        //
        if (empty($parentStepCategory['entity_id'])) {
            throw new LogicException('stepCategory data is invalid', ErrorCode::DATA_INVALID);
        }
        $module = module_by_id($parentStepCategory['entity_id']);
        if (empty($module)) {
            throw new LogicException('stepCategory data is invalid module not found', ErrorCode::DATA_INVALID);
        }
        $moduleStepWorkflowCodeList = [];
        if (in_array($module['code'], ['design', 'asset', 'level'])) {
            $moduleStepWorkflowCodeList = ['design', 'asset', 'level'];
        } elseif (in_array($module['code'], ['shot'])) {
            $moduleStepWorkflowCodeList = ['motion_shot'];
        }
        if (!empty($moduleStepWorkflowCodeList)) {
            $res = $this->getModuleStepCategoryRelationList($moduleStepWorkflowCodeList, $projectId);
        } else {
            $res = StepCategoryService::getInstance()->getList(['id' => $stepCategory['id']], 'id,name,code,color', 'show_index asc');
        }

        return $res;
    }

    /**
     * 查询模块环节上下游关系列表
     * @param array $moduleCodeList
     * @param int $projectId
     * @return array
     */
    public function getModuleStepCategoryRelationList(array $moduleCodeList, int $projectId)
    {
        // 查询指定的字段
        $fields = 'id,project_id,name,code,step_workflow_config as config';
        //
        $stepWorkFlowList = StepWorkflowService::getInstance()
            ->getList(['project_id' => ['IN', [0, $projectId]], 'code' => ['IN', $moduleCodeList], 'is_enable' => 'yes'], $fields);

        $workflowIds = [];
        $stepWorkFlowMap = [];
        foreach ($stepWorkFlowList as $workflowItem) {
            // 默认取项目id为0的工序流配置,如果有配置项目,就取项目的工序流配置
            if (empty($stepWorkFlowMap[$workflowItem['code']] ?? []) || $stepWorkFlowMap[$workflowItem['code']]['project_id'] == 0) {
                $stepWorkFlowMap[$workflowItem['code']] = $workflowItem;
                $workflowIds[] = $workflowItem['id'];
            }
        }
        $stepList = StepService::getInstance()->getList([], 'id,name,code,step_category_id');
        $stepList = array_column($stepList, 'step_category_id', 'code');
        $downstreamStepCategoryIdList = [];
        foreach ($moduleCodeList as $moduleCode) {
            $workflowNodes = $stepWorkFlowMap[$moduleCode] ?? '{}';
            $workflowNodes = json_decode($workflowNodes['config'], true);
            if (empty($workflowNodes['nodes'])) {
                continue;
            }
            $workflowNodes = $workflowNodes['nodes'];
            $firstStepCategoryId = 0;
            foreach (array_shift($workflowNodes) as $node) {
                if (empty($node['module_code']) || empty($stepList[$node['module_code']])) {
                    continue;
                }
                $firstStepCategoryId = $stepList[$node['module_code']];
                break;
            }
            if ($firstStepCategoryId <= 0) {
                continue;
            }
            $currentDownstreamStepCategoryIdList = StepCategoryDownStreamService::getInstance()->get($projectId, $firstStepCategoryId, $workflowIds);
            array_unshift($currentDownstreamStepCategoryIdList, $firstStepCategoryId);
            $currentDownstreamStepCategoryIdList = array_values(array_unique($currentDownstreamStepCategoryIdList));
            $downstreamStepCategoryIdList = array_merge($downstreamStepCategoryIdList, $currentDownstreamStepCategoryIdList);
        }
        if (empty($downstreamStepCategoryIdList)) {
            return [];
        }
        $stepCategoryList = StepCategoryService::getInstance()->getList(['id' => ['IN', $downstreamStepCategoryIdList], 'is_show' => 'yes'], 'id,name,code,color','show_index asc');

        return $stepCategoryList;
    }

    /**
     * 制片确认延期单
     * @param $delayOrderIdList
     * @param string $confirmStatus
     * @param int $userId
     * @return array
     * @throws Exception
     * @throws Throwable
     */
    public function managerConfirm($delayOrderIdList, string $confirmStatus, int $userId)
    {
        $delayOrderTaskIdList = model(DelayOrderTaskModel::class)
            ->where(['delay_order_id' => ['IN', $delayOrderIdList], 'manager_confirmed' => 'default'])
            ->select();
        $dataList = array_map(function ($i) use ($confirmStatus) {
            return ['id' => $i['id'], 'is_pass' => $confirmStatus];
        }, $delayOrderTaskIdList);
        return DelayOrderTaskStepCategoryConfirmService::getInstance()->managerConfirm($dataList, $userId);
    }


    /**
     * 获取延期单延期任务分组
     * @param $param
     * @return array
     */
    public function getDelayOrderListThumbnail($param): array
    {
        $dbParam = query_array_construct([
            "filter" => "",
            "field" => "strval",
            "page" => "split_array|array_map,intval",
        ], $param['param'] ?? [], [
            "filter" => [],
            "field" => "",
            "page" => [1, C('default_page_total')],
        ]);
        $param = query_array_construct([
            "project_id" => "intval",
            "department_id" => "intval",
            "episode_code" => "split_array",
            "step_category_id" => "split_array|array_map,intval",
            "start_time" => "intval",
            "end_time" => "intval",
        ], $param);
        $param = array_merge($param, $dbParam);
        if (!empty($param['project_id'])) {
            $param['project_id'] = ProjectService::getInstance()->getProjectParentProjectId($param['project_id']);
        }
        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus()['id'];
        $delayOrderList = $this->getDelayOrderListThumbnailDbr($param)
            ->where([
                "JSON_UNQUOTE(JSON_EXTRACT(task.json,'$.task_status'))" => ["NEQ", $closedStatus],
                "dot.type" => "request_delay",
                "dot.manager_confirmed" => "yes",
            ])
            ->field([
                "delay_order.id id", "task.project_id",
                "entity.initial_episode_code",
                "episode.id as episode_id",
                "task.step_category_id",
                "count(distinct task.id) as delay_count",
                "min(if(dot.original_plan_time <> 0, dot.original_plan_time, null)) start_time",
                "max(if(dot.delay_time <> 0, dot.delay_time, null)) end_time",
            ])->group('delay_order.id, entity.initial_episode_code, task.step_category_id')
//            ->page(...$param['page'])
            ->select();
        $stepCategoryShowList = TemplateService::getInstance()->findStepCategoryShowList();
        $stepCategoryGroup = [];
        $stepCategoryGroupMap = [];
        foreach ($stepCategoryShowList as $stepCategoryItem) {
            $stepCategoryGroup[$stepCategoryItem['show_name']][] = $stepCategoryItem['id'];
        }
        foreach ($stepCategoryGroup as $showName => $stepCategoryIds) {
            sort($stepCategoryIds);
            foreach ($stepCategoryIds as $id) {
                $stepCategoryGroupMap[$id] = [
                    'id' => $showName,
                    'name' => $showName,
                    'children_ids' => $stepCategoryIds,
                ];
            }
        }
        $res = [];
        foreach ($delayOrderList as $delayOrderItem) {
            if (!isset($stepCategoryGroupMap[$delayOrderItem['step_category_id']])) {
                continue;
            }
            if (!empty($delayOrderItem['delay_count'])
                && !empty($delayOrderItem['start_time']) && !empty($delayOrderItem['end_time'])) {
                $stepCategory = $stepCategoryGroupMap[$delayOrderItem['step_category_id']];
                $stepCategoryIds = $stepCategory['children_ids'];
                array_unshift($stepCategoryIds, "ep_" . $delayOrderItem['episode_id'], "id_" . $delayOrderItem['id']);
                $stepCategoryKey = implode('#', $stepCategoryIds);
                if (!isset($res[$stepCategoryKey])) {
                    $delayOrderItem['step_category'] = $stepCategory;
                    $delayOrderItem['step_category_id'] = [$delayOrderItem['step_category_id']];
                    $res[$stepCategoryKey] = $delayOrderItem;
                } else {
                    if (!in_array($delayOrderItem['step_category_id'], $res[$stepCategoryKey]['step_category_id'])) {
                        $res[$stepCategoryKey]['step_category_id'][] = $delayOrderItem['step_category_id'];
                    }
                    $res[$stepCategoryKey]['start_time'] = min($res[$stepCategoryKey]['start_time'], $delayOrderItem['start_time']);
                    $res[$stepCategoryKey]['end_time'] = max($res[$stepCategoryKey]['end_time'], $delayOrderItem['end_time']);
                    $res[$stepCategoryKey]['delay_count'] += $delayOrderItem['delay_count'];
                }
            }
        }
        return result_array_construct([
            '_final' => 'array_values',
            'id' => 'intval',
            'project_id' => 'intval',
            'episode_id' => 'intval',
            'step_category_id' => 'split_array|array_map,intval|array_filter|array_unique',
            'step_category' => '',
            'initial_episode_code' => '',
            'delay_count' => 'intval',
            'start_time' => 'intval',
            'end_time' => 'intval',
        ], $res);
    }

    /**
     * 获取延期单dbr
     * @param $param
     * @return array|object|Model|RelationModel
     */
    private function getDelayOrderListThumbnailDbr($param)
    {
        $entityModuleId = module_by_code('episode')['id'];
        $dbr = model(DelayOrderModel::class)
            ->join('delay_order_task as dot on delay_order.id = dot.delay_order_id', 'left')
            ->join('task on task.id = dot.task_id')
            ->join('entity on entity.id = task.entity_id', 'left')
            ->join('(select id,code,project_id from entity where module_id = ' . $entityModuleId . ' ) as episode on episode.code = entity.initial_episode_code and episode.project_id = entity.project_id');
        // 项目
        if (!empty($param['project_id'])) {
            $dbr = $dbr->where(['entity.project_id' => $param['project_id'],
                'task.project_id' => $param['project_id']]);
        }
        // 剧集
        if (!empty($param['episode_code'])) {
            $dbr = $dbr->where(['entity.initial_episode_code' => ['IN', $param['episode_code']]]);
        }
        // 环节筛选
        if (!empty($param['step_category_id'])) {
            $dbr = $dbr->where(['task.step_category_id' => ['in', implode(',', $param['step_category_id'])]]);
        }
        // 部门筛选
        if (!empty($param['department_id'])) {
            $departmentUserData = TenantService::getInstance()->selectDepartmentUser(request()->getTenantId(), [
                'param' => ['filter' => ['department_id' => $param['department_id'],]]
            ]);
            $saasUserIdList = array_column($departmentUserData['rows'], 'saas_user_id');
            if (empty($saasUserIdList)) {
                return $dbr->where("1 = 0");
            }
            $dbr = $dbr->join('task_member on task.id = task_member.task_id', 'left')
                ->where(['task_member.partner' => ['in', implode(',', $saasUserIdList)]]);
        }
//        // 个人
//        if (empty($param['step_category_id']) && empty($param['department_id'])) {
//            $dbr = $dbr->join('task_member on task.id = task_member.task_id', 'left')
//                ->where(['task_member.partner' => request()->getUserId()]);
//        }
        if (!empty($param['start_time'] && !empty($param['end_time']))) {
            $dbr = $dbr->where([
                'dot.original_plan_time' => ['BETWEEN', [$param['start_time'], $param['end_time']]],
            ]);
        }
        return $dbr;
    }
}
